<?php

namespace SinoPHP\BbdBoss\Api;

use Exception;
use LogicException;
use ErrorException;
use Upfor\UpforPHP\Controller;
use SinoPHP\BbdBoss\Defines\OutApiCode;
use SinoPHP\BbdBoss\Traits\ApiParamsValidate;

/**
 * API抽象类
 *
 * WARNING: 仅用作对外API, 对内API请走MVC路由
 *
 * - 状态码统一放置在`\SinoPHP\BbdBoss\Defines\OutApiCode`
 * - 状态码必须根据业务进行分类, 且描述准确
 * - 状态码的抛出, 必须由异常抛出
 * - 抛异常时, 必须携带`code`, 以便API能统一接收并返回给客户端
 * - 如果不指定抛出异常的`message`, 则由对应状态码的定义信息代替
 */
abstract class ApiAbstract extends Controller {

    // 使用接口参数校验Traits
    use ApiParamsValidate;

    /**
     * 接口请求数据
     *
     * @var array
     */
    protected $postData = array();

    /**
     * 请求参数
     *
     * @var array
     */
    protected $requestParams = array();

    /**
     * API版本(方法)
     *
     * @var string
     */
    protected $versionMethod;

    /**
     * 接口默认版本(各API可自定义重写覆盖)
     *
     * @var string
     */
    protected $defaultVersion = '1.0.0';

    /**
     * 当前请求时间
     *
     * @var integer
     */
    protected $currentTime;

    public function __construct() {
        parent::__construct();

        $this->currentTime = request()->getVar('REQUEST_TIME', time());

        // 验证请求方式
        if (!request()->isPost()) {
            $this->error(101001);
        }

        $this->parseParams();

        $this->buildVersionMethod();
    }

    /**
     * API版本分发
     */
    public function index() {
        try {

            // 执行接口逻辑
            $this->{$this->versionMethod}();

        } catch (Exception $e) {
            // 接收自定义报错, 不定义CODE且在调试状态, 则视为系统错误, 直接抛出错误
            if ($e->getCode() > 100000) {
                // 接口业务逻辑错误
                $this->error($e->getCode(), $e->getMessage() ? : null);
            } elseif (!config('debug')) {
                // 非调试状态, 记录错误信息
                logInfo('ERROR', 'Message: ' . $e->getMessage() . ' File: ' . $e->getFile() . ' Line: ' . $e->getLine());
                $this->error(101000, $e->getMessage() ? : null);
            } else {
                // 系统错误
                throw new ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine(), $e->getPrevious());
            }
        }
    }

    /**
     * 解析参数
     */
    private function parseParams() {
        $this->postData = (array) request()->post();

        $this->requestParams = &$this->postData['data'];
    }

    /**
     * 构建版本控制
     */
    private function buildVersionMethod() {
        $this->versionMethod = 'v_' . str_replace('.', '_', $this->defaultVersion);

        if (!empty($this->postData['version']) && version_compare($this->postData['version'], '0.0.1') >= 0) {
            $versionMethod = 'v_' . str_replace('.', '_', $this->postData['version']);
            if (method_exists($this, $versionMethod)) {
                $this->versionMethod = $versionMethod;
            }
        }
    }

    /**
     * 失败返回
     *
     * @param integer $code
     * @param string  $msg
     */
    public function error($code, $msg = null) {
        static $apiCodeStatus = array();
        if (!$apiCodeStatus) {
            $apiCodeStatus = OutApiCode::$statusCode;
        }

        if (!isset($apiCodeStatus[$code])) {
            throw new LogicException('Unknown api error code');
        }

        if (!$msg) {
            $msg = $apiCodeStatus[$code];
        }
        $this->response([], $code, $msg);
    }

}
